export {};

// Define a TypeScript interface
interface Person<T = string, K = number> {
	name: string;
	age: number;
}

type Tuple = [ foo: number, bar: String | Boolean];

// Create an array of objects with the defined interface
const people: Person[] = [
	{ name: "Alice", age: 30 },
	{ name: "Bob", age: 25 },
	{ name: "Charlie",
		age: 35 },
];

var log = console.log;

// Use a for...of loop to iterate over the array
for (const person of people) {
	log(`Hello, my name is ${person.name} and I am ${person.age} years old.`);
}

// Define a generic function
function identity<T>(arg: T): T {
	return arg;
}

// TypeScript enum
enum EnumFoo {
	aaa,
	bbb,
	ccc,
}

// Use the generic function with type inference
const result = identity(
	"TypeScript is awesome");
log(result);

// Use optional properties in an interface
interface Car {
	make: string;
	model?: string;
}

// Create objects using the interface
const car1: Car = { make: "Toyota" };
const car2: Car = {
	make: "Ford",
	model: "Focus" };

// Use union types
type Fruit = "apple" | "banana" | "orange";
const favoriteFruit: Fruit = "apple";

// Use a type assertion to tell TypeScript about the type
const inputValue: any = "42";
const numericValue = inputValue as number;

// Define a class with access modifiers
class Animal {
	private name: string;
	constructor(name: string) {
		this.name = name;
	}

	protected makeSound(sound: string) {
		log(`${this.name} says ${sound}`);
	}
}

// Extend a class
class Dog extends Animal {
	constructor(private alias: string) {
		super(alias);
	}

	bark() {
		this.makeSound("Woof!");
	}
}

// Decorators
class Bar {
	@log
	baz() {
		return "qux";
	}
}

const dog = new Dog("Buddy");
dog.bark();

var fn = (): string => {
	return "hello" + 1;
};

log(car1, car2, favoriteFruit, numericValue, fn());

// Generator
export function* generator1() {
	let id = 0;
	while (id < 100) {
		yield id++;
	}
}
export function* generator2() {
	yield* generator1();
}